import VNode, { createTextVNode } from 'core/vdom/vnode'
import {
  isFalse,
  isTrue,
  isArray,
  isDef,
  isUndef,
  isPrimitive
} from 'shared/util'

// The template compiler attempts to minimize the need for normalization by
// statically analyzing the template at compile time.
//
// For plain HTML markup, normalization can be completely skipped because the
// generated render function is guaranteed to return Array<VNode>. There are
// two cases where extra normalization is needed:

// 1. When the children contains components - because a functional component
// may return an Array instead of a single root. In this case, just a simple
// normalization is needed - if any child is an Array, we flatten the whole
// thing with Array.prototype.concat. It is guaranteed to be only 1-level deep
// because functional components already normalize their own children.
/**
 * 1.当子级包含组件时-因为函数组件可能返回一个数组而不是单个根。
 * 在这种情况下，只需要一个简单的规范化——如果任何子对象都是数组，
 * 我们用Array.prototype.concat将整个对象展平。它保证只有1级深度，因为功能组件已经将自己的子级标准化了。
 * @param children 
 * @returns 
 */
export function simpleNormalizeChildren(children: any) {
  for (let i = 0; i < children.length; i++) {
    if (isArray(children[i])) {
      return Array.prototype.concat.apply([], children)
    }
  }
  return children
}

// 2. When the children contains constructs that always generated nested Arrays,
// e.g. <template>, <slot>, v-for, or when the children is provided by user
// with hand-written render functions / JSX. In such cases a full normalization
// is needed to cater to all possible types of children values.

/**
 * 2.当子对象包含总是生成嵌套数组的构造时，例如，
 * <template>、<slot>、v-for，或者当用户为孩子提供手写的渲染函数/JSX时。
 * 在这种情况下，需要完全规范化，以满足所有可能类型的儿童价值观。
 * @param children 
 * @returns 
 */
export function normalizeChildren(children: any): Array<VNode> | undefined {
  return isPrimitive(children) ? [ createTextVNode(children) ] : 
        isArray(children) ? normalizeArrayChildren(children) : undefined
}

function isTextNode(node): boolean {
  return isDef(node) && isDef(node.text) && isFalse(node.isComment)
}

// 规范化数组子对象
function normalizeArrayChildren(
  children: any,
  nestedIndex?: string
): Array<VNode> {
  const res: VNode[] = []

  let i, c, lastIndex, last
  for (i = 0; i < children.length; i++) {
    c = children[i]
    if (isUndef(c) || typeof c === 'boolean') continue
    lastIndex = res.length - 1
    last = res[lastIndex]

    //  nested
    // 递归 规范化数组子对象
    if (isArray(c)) {
      if (c.length > 0) {
        c = normalizeArrayChildren(c, `${nestedIndex || ''}_${i}`)
        // 合并相邻的文本节点
        // merge adjacent text nodes
        
        if (isTextNode(c[0]) && isTextNode(last)) {
          res[lastIndex] = createTextVNode(last.text + c[0].text)
          c.shift()
        }

        res.push.apply(res, c)
      }

    } else if (isPrimitive(c)) {

      if (isTextNode(last)) {
        // merge adjacent text nodes
        // this is necessary for SSR hydration because text nodes are
        // essentially merged when rendered to HTML strings
        // 这对于SSR水合是必要的，因为文本节点在呈现为HTML字符串时基本上是合并的
        res[lastIndex] = createTextVNode(last.text + c)

      } else if (c !== '') {
        // convert primitive to vnode
        // 将原语转换为vnode
        res.push(createTextVNode(c))
      }
    } else {
      if (isTextNode(c) && isTextNode(last)) {
        // merge adjacent text nodes
        res[lastIndex] = createTextVNode(last.text + c.text)
      } else {
        // default key for nested array children (likely generated by v-for)
        // 嵌套数组子项的默认键（可能由v-for生成）
        if (
          isTrue(children._isVList) &&
          isDef(c.tag) &&
          isUndef(c.key) &&
          isDef(nestedIndex)
        ) {
          c.key = `__vlist${nestedIndex}_${i}__`
        }
        res.push(c)
      }
    }
  }
  return res
}
